﻿using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using XAMSDK.Types;
using System.IO;
using System.Threading;

namespace XAMSDK
{
    /// <summary>
    /// Abstract base class for XAMLibraryHandle, XSystem and XSet objects i.e. objects which have fields
    /// </summary>
    public abstract class XAMObject : System.Collections.IEnumerable, IDisposable
    {
        // Holds a list of objects created by the factory e.g. XSystems created by XAM_Library
        protected List<XAMObject> factoryObjects = new List<XAMObject>();
        internal XAMHandle xamHandle;
        internal void RemoveFactoryObject(XAMObject xobj) { factoryObjects.Remove(xobj); }

        internal List<XPolicy> policyList = new List<XPolicy>();

        /// <summary>
        /// Internal class for transferring entire contents of a single XStream part of a
        /// MultipartXStream to / from a standard Stream via a dedicated thread
        /// </summary>
        internal class MultipartThread
        {
            private Thread myThread;

            /// <summary>
            /// Writes the part of input stream to an output stream
            /// An XStream can be either the input or output stream (NOT both)
            /// </summary>
            /// <param name="input">stream to be read from</param>
            /// <param name="output">stream to be written to</param>
            /// <param name="offset">offset within the standard Stream to read / write the XStream content</param>
            /// <param name="length">length of the XStream content</param>
            internal MultipartThread(Stream input, Stream output, long offset, int length)
            {
                myThread = new Thread(delegate()
                {
                    byte[] buffer;
                    System.Type inputType = input.GetType();
                    BinaryReader myReader = new BinaryReader(input);

                    // We need to set either the input or output position of the "regular" stream 
                    if (inputType.Name == "XStream")
                    {
                        // Reading from an XStream
                        buffer = myReader.ReadBytes(length);

                        // Set to the correct offset in the output stream
                        lock (output)
                        {
                            output.Seek(offset, SeekOrigin.Begin);
                            output.Write(buffer, 0, buffer.Length);
                        }
                    }
                    else
                    {
                        // Set to the correct offset in the input stream and read the required bytes
                        lock (input)
                        {
                            input.Seek(offset, SeekOrigin.Begin);
                            buffer = myReader.ReadBytes(length);
                        }

                        // Write the data to our XStream
                        output.Write(buffer, 0, buffer.Length);
                    }
                });
            }

            /// <summary>
            /// Implicit cast of MultipartThread to a Thread
            /// </summary>
            /// <param name="t">MultipartThread</param>
            /// <returns>Thread</returns>
            static public implicit operator Thread(MultipartThread t)
            {
                return t.myThread;
            }
        }

        #region Methods
        /// <summary>
        /// Public method to request that a derived class populates the policyList 
        /// </summary>
        /// 
        public virtual void GetPolicies() { }

        /// <summary>
        /// Populates the policyList with policies of a given type via the filter provided.
        /// </summary>
        /// <param name="t">Types of policy to be created</param>
        /// <param name="filter">Filter which will match policies of the relevant PolicyType</param>
        protected void GetPolicies(XPolicyType t, string filter)
        {
            using (XIterator xit = CreateXIterator(filter))
            {
                while (xit.HasNext)
                {
                    XString f = new XString(xit.Next);

                    policyList.Add(new XPolicy(f, t));
                }
            }
        }

        /// <summary>
        /// Writes the entire contents of a stream to a multipart XStream via multiple threads
        /// Stream should not be added to as only the length of the stream when this method is called will be transferred!
        /// </summary>
        /// <param name="s">stream containing the content to be written</param>
        /// <param name="fieldName">name of the MultipartXStream</param>
        /// <param name="mimeType">MIME type of the field</param>
        /// <param name="parts">number of parts to split the stream inot (and, indirectly, number of threads to be used)</param>
        /// <param name="binding">is the XStream field to be binding?</param>
        public void MultipartXStreamWrite(Stream s, XString fieldName, XString mimeType, int parts, bool binding)
        {
            using (Helpers.MultipartXStream str = CreateMultipartXStream(fieldName, mimeType, parts, s.Length, binding))
            {
                long offset = 0;
                int i = 0;
                Thread[] t = new Thread[parts];

                for (i = 0; i < parts; i++)
                {
                    t[i] = new MultipartThread(s, str.streams[i], offset, (int)str.PartLength(i));
                    t[i].Start();

                    offset += str.PartLength(i);
                }

                for (i = 0; i < parts; i++)
                    t[i].Join();
            }
        }

        /// <summary>
        /// Writes the entire contents of a stream to a multipart XStream via multiple threads
        /// Stream should not be added to as only the length of the stream when this method is called will be transferred!
        /// Convenience overload which creates the field as binding.
        /// </summary>
        /// <param name="s">stream containing the content to be written</param>
        /// <param name="fieldName">name of the MultipartXStream</param>
        /// <param name="mimeType">MIME type of the field</param>
        /// <param name="parts">number of parts to split the stream inot (and, indirectly, number of threads to be used)</param>
        public void MultipartXStreamWrite(Stream s, XString fieldName, XString mimeType, int parts)
        {
            MultipartXStreamWrite(s, fieldName, mimeType, parts, true);
        }

        /// <summary>
        /// Reads the entire contents of a MultipartXStream to a stream via multiple threads (one for each part)
        /// </summary>
        /// <param name="s">stream for the content to be written to</param>
        /// <param name="fieldName">name of the MultipartXStream</param>
        public void MultipartXStreamRead(Stream s, XString fieldName)
        {
            long offset = 0;
            int i = 0;

            using (Helpers.MultipartXStream str = OpenMultipartXStream(fieldName))
            {
                int parts = (int)str.streams.Length;
                Thread[] t = new Thread[parts];

                for (i = 0; i < parts; i++)
                {
                    t[i] = new MultipartThread(str.streams[i], s, offset, (int)str.PartLength(i));
                    t[i].Start();

                    offset += str.PartLength(i);
                }

                for (i = 0; i < parts; i++)
                    t[i].Join();
            }
        }

        /// <summary>
        /// Deletes (removes) an existing field from a XAMObject
        /// </summary>
        /// <param name="fieldName">name of the field to deleted.</param>
        public void DeleteField(XString fieldName)
        {
            int retval = Imports.XAM_DeleteField(xamHandle, fieldName);
            Helpers.CheckAndThrow(retval, "XAM_DeleteField");
        }

        /// <summary>
        /// Tests for the existence of a field an a XAMObject.
        /// </summary>
        /// <param name="fieldName">name of the field we are checking for.</param>
        /// <returns>true if the field exists.</returns>
        public bool ContainsField(XString fieldName)
        {
            bool contains = false;
            int retval = Imports.XAM_ContainsField(xamHandle, fieldName, out contains);
            Helpers.CheckAndThrow(retval, "XAM_ContainsField");
            return contains;
        }

        /// <summary>
        /// Sets the binding status of an existing field on a XAMObject
        /// </summary>
        /// <param name="fieldName">name of the field to set the binding status on</param>
        /// <param name="binding">binding status of the field (true == binding)</param>
        public void SetBinding(XString fieldName, bool binding)
        {
            if (binding)
            {
                int retval = Imports.XAM_SetFieldAsBinding(xamHandle, fieldName);
                Helpers.CheckAndThrow(retval, "XAM_SetFieldAsBinding");
            }
            else
            {
                int retval = Imports.XAM_SetFieldAsNonbinding(xamHandle, fieldName);
                Helpers.CheckAndThrow(retval, "XAM_SetFieldAsNonbinding");
            }

        }

        /// <summary>
        /// Is the field a Property?
        /// </summary>
        /// <param name="fieldName">name of the field we are examining.</param>
        /// <returns>true if the field is a property.</returns>
        public bool IsStream(XString fieldName)
        {
            bool stream = false;
            int retval = Imports.XAMToolkit_IsXStreamField(xamHandle, fieldName, out stream);
            Helpers.CheckAndThrow(retval, "XAMToolkit_IsPropertyField");
            return stream;
        }

        /// <summary>
        /// Is the field an XStream?
        /// </summary>
        /// <param name="fieldName">name of the field we are examining.</param>
        /// <returns>true if the field is an XStream.</returns>
        public bool IsProperty(XString fieldName)
        {
            bool property = false;
            int retval = Imports.XAMToolkit_IsPropertyField(xamHandle, fieldName, out property);
            Helpers.CheckAndThrow(retval, "XAMToolkit_IsPropertyField");
            return property; ;
        }

        /// <summary>
        /// Destructor
        /// </summary>
        ~XAMObject()
        {
            Dispose(false);
        }

        /// <summary>
        /// Derived classes can override this method to perform any special processing / cleanup (nothing needed for this base class)
        /// </summary>
        protected virtual void Close() { }


        #endregion

        #region Properties
        /// <summary>
        /// Public accessor for the PolicyList enumerator - do not use this to modify the List!
        /// </summary>
        public List<XPolicy>.Enumerator PolicyEnumerator
        {
            get
            {
                return policyList.GetEnumerator();
            }
        }
        #endregion

        #region IDisposable
        protected bool disposed = false;
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        // The "disposing" boolean allows derived objects to add an additional step to dispose
        // of any IDisposable objects they own dependent on who is calling it i.e. diectly or via the dtor.
        public void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Cleanup managed objects
                foreach (XAMObject i in factoryObjects)
                {
                    if (!i.disposed)
                        i.Dispose();
                }

                factoryObjects.RemoveAll(delegate(XAMObject obj) { return true; } );
            }

            if (!disposed)
            {
                // Cleanup any unmanaged objects
                Close();
                disposed = true;
            }
        }
        #endregion

        #region IEnumerable
        public IEnumerator GetEnumerator()
        {
            return CreateXIterator("");
        }

        #endregion

        #region XString Field methods
        /// <summary>
        /// Creates an XString Property field on a XAMObject
        /// </summary>
        /// <param name="fieldName">name of the field to be created</param>
        /// <param name="fieldValue">value of the field</param>
        /// <param name="binding">binding status of the field</param>
        public void CreateField(XString fieldName, XString fieldValue, bool binding)
        {
            int retval = Imports.XAM_CreateString(xamHandle, fieldName, binding, fieldValue);
            Helpers.CheckAndThrow(retval, "XAM_CreateString");
        }

        /// <summary>
        /// Convenience method which creates an XString Property field on a XAMObject with a default status of binding 
        /// </summary>
        /// <param name="fieldName">name of the field to be created</param>
        /// <param name="fieldValue">value of the field</param>
        public void CreateField(XString fieldName, XString fieldValue) { this.CreateField(fieldName, fieldValue, true); }

        /// <summary>
        /// Sets the value of an XString Property field
        /// </summary>
        /// <param name="fieldName">name of the field to be set</param>
        /// <param name="fieldValue">value the field is to be set to</param>
        public void SetField(XString fieldName, XString fieldValue)
        {
            int retVal = Imports.XAM_SetString(xamHandle, fieldName, fieldValue);
            Helpers.CheckAndThrow(retVal, "XAM_SetString");
        }

        /// <summary>
        /// Gets the value of an XString Property field
        /// </summary>
        /// <param name="fieldName">name of the field to get</param>
        /// <param name="fieldValue">current value of the field</param>
        public void GetField(XString fieldName, XString fieldValue)
        {
            int retval = Imports.XAM_GetString(xamHandle, fieldName, fieldValue);
            Helpers.CheckAndThrow(retval, "XAM_GetString");
        }

        
        public void GetFieldBytes(XString fieldName, ref XString fieldValue)
        {
            byte[] srcBytes = new byte[Constants.STRING_SZ];
            int retval = Imports.XAM_GetString(xamHandle, fieldName, srcBytes);
            Helpers.CheckAndThrow(retval, "XAM_GetString");

            String someString = Encoding.Default.GetString(Encoding.Convert(Encoding.UTF8, Encoding.Default, srcBytes));
            
            fieldValue = new XString(someString.Substring(0, someString.IndexOf('\0')));
        }
        
        #endregion

        #region XUID field methods
        /// <summary>
        /// Creates an XUID Property field on a XAMObject
        /// </summary>
        /// <param name="fieldName">name of the field to be created</param>
        /// <param name="fieldValue">value of the field</param>
        /// <param name="binding">binding status of the field</param>
        public void CreateField(XString fieldName, XUID fieldValue, bool binding)
        {
            int retval = Imports.XAM_CreateXUID(xamHandle, fieldName, binding, fieldValue);
            Helpers.CheckAndThrow(retval, "XAM_CreateXUID");
        }

        /// <summary>
        /// Convenience method which creates an XUID Property field on a XAMObject with a default status of binding 
        /// </summary>
        /// <param name="fieldName">name of the field to be created</param>
        /// <param name="fieldValue">value of the field</param>
        public void CreateField(XString fieldName, XUID fieldValue) { this.CreateField(fieldName, fieldValue, true); }

        /// <summary>
        /// Sets the value of an XUID Property field
        /// </summary>
        /// <param name="fieldName">name of the field to be set</param>
        /// <param name="fieldValue">value the field is to be set to</param>
        public void SetField(XString fieldName, XUID fieldValue)
        {
            int retVal = Imports.XAM_SetXUID(xamHandle, fieldName, fieldValue);
            Helpers.CheckAndThrow(retVal, "XAM_SetXUID");
        }

        /// <summary>
        /// Gets the value of an XUID Property field
        /// </summary>
        /// <param name="fieldName">name of the field to get</param>
        /// <param name="fieldValue">current value of the field</param>
        public void GetField(XString fieldName, XUID fieldValue)
        {
            int retval = Imports.XAM_GetXUID(xamHandle, fieldName, fieldValue);
            Helpers.CheckAndThrow(retval, "XAM_GetXUID");
        }
        #endregion

        #region DateTime field methods
        /// <summary>
        /// Creates an DateTime Property field on a XAMObject
        /// </summary>
        /// <param name="fieldName">name of the field to be created</param>
        /// <param name="fieldValue">value of the field</param>
        /// <param name="binding">binding status of the field</param>
        public void CreateField(XString fieldName, DateTime fieldValue, bool binding)
        {
            XString str = fieldValue.ToString();
            int retval = Imports.XAM_CreateDatetime(xamHandle, fieldName, binding, str);
            Helpers.CheckAndThrow(retval, "XAM_CreateDatetime");
        }

        /// <summary>
        /// Convenience method which creates an DateTime Property field on a XAMObject with a default status of binding 
        /// </summary>
        /// <param name="fieldName">name of the field to be created</param>
        /// <param name="fieldValue">value of the field</param>
        public void CreateField(XString fieldName, DateTime fieldValue) { this.CreateField(fieldName, fieldValue, true); }

        /// <summary>
        /// Sets the value of an DateTime Property field
        /// </summary>
        /// <param name="fieldName">name of the field to be set</param>
        /// <param name="fieldValue">value the field is to be set to</param>
        public void SetField(XString fieldName, DateTime fieldValue)
        {
            XString str = fieldValue.ToString();
            int retVal = Imports.XAM_SetDatetime(xamHandle, fieldName, str);
            Helpers.CheckAndThrow(retVal, "XAM_SetDatetime");
        }

        /// <summary>
        /// Gets the value of an DateTime Property field
        /// </summary>
        /// <param name="fieldName">name of the field to get</param>
        /// <param name="fieldValue">current value of the field</param>
        public void GetField(XString fieldName, out DateTime fieldValue)
        {
            XString str = new XString();
            int retval = Imports.XAM_GetDatetime(xamHandle, fieldName, str);
            Helpers.CheckAndThrow(retval, "XAM_GetDatetime");
            fieldValue = DateTime.Parse(str);
        }
        #endregion

        #region Bool field methods
        /// <summary>
        /// Creates an Bool Property field on a XAMObject
        /// </summary>
        /// <param name="fieldName">name of the field to be created</param>
        /// <param name="fieldValue">value of the field</param>
        /// <param name="binding">binding status of the field</param>
        public void CreateField(XString fieldName, bool fieldValue, bool binding)
        {
            int retval = Imports.XAM_CreateBoolean(xamHandle, fieldName, binding, fieldValue);
            Helpers.CheckAndThrow(retval, "XAM_CreateBoolean");
        }

        /// <summary>
        /// Convenience method which creates an Bool Property field on a XAMObject with a default status of binding 
        /// </summary>
        /// <param name="fieldName">name of the field to be created</param>
        /// <param name="fieldValue">value of the field</param>
        public void CreateField(XString fieldName, bool fieldValue) { this.CreateField(fieldName, fieldValue, true); }

        /// <summary>
        /// Sets the value of an Bool Property field
        /// </summary>
        /// <param name="fieldName">name of the field to be set</param>
        /// <param name="fieldValue">value the field is to be set to</param>
        public void SetField(XString fieldName, bool fieldValue)
        {
            int retVal = Imports.XAM_SetBoolean(xamHandle, fieldName, fieldValue);
            Helpers.CheckAndThrow(retVal, "XAM_SetBoolean");
        }

        /// <summary>
        /// Gets the value of an Bool Property field
        /// </summary>
        /// <param name="fieldName">name of the field to get</param>
        /// <param name="fieldValue">current value of the field</param>
        public void GetField(XString fieldName, out bool fieldValue)
        {
            int retval = Imports.XAM_GetBoolean(xamHandle, fieldName, out fieldValue);
            Helpers.CheckAndThrow(retval, "XAM_GetBoolean");
        }
        #endregion

        #region Double field methods
        /// <summary>
        /// Creates an Double Property field on a XAMObject
        /// </summary>
        /// <param name="fieldName">name of the field to be created</param>
        /// <param name="fieldValue">value of the field</param>
        /// <param name="binding">binding status of the field</param>
        public void CreateField(XString fieldName, double fieldValue, bool binding)
        {
            int retval = Imports.XAM_CreateDouble(xamHandle, fieldName, binding, fieldValue);
            Helpers.CheckAndThrow(retval, "XAM_CreateDouble");
        }

        /// <summary>
        /// Convenience method which creates an Double Property field on a XAMObject with a default status of binding 
        /// </summary>
        /// <param name="fieldName">name of the field to be created</param>
        /// <param name="fieldValue">value of the field</param>
        public void CreateField(XString fieldName, double fieldValue) { this.CreateField(fieldName, fieldValue, true); }

        /// <summary>
        /// Sets the value of an Double Property field
        /// </summary>
        /// <param name="fieldName">name of the field to be set</param>
        /// <param name="fieldValue">value the field is to be set to</param>
        public void SetField(XString fieldName, double fieldValue)
        {
            int retVal = Imports.XAM_SetDouble(xamHandle, fieldName, fieldValue);
            Helpers.CheckAndThrow(retVal, "XAM_SetDouble");
        }

        /// <summary>
        /// Gets the value of an Double Property field
        /// </summary>
        /// <param name="fieldName">name of the field to get</param>
        /// <param name="fieldValue">current value of the field</param>
        public void GetField(XString fieldName, out double fieldValue)
        {
            int retval = Imports.XAM_GetDouble(xamHandle, fieldName, out fieldValue);
            Helpers.CheckAndThrow(retval, "XAM_GetDouble");
        }
        #endregion

        #region Int field methods
        /// <summary>
        /// Creates an Int Property field on a XAMObject
        /// </summary>
        /// <param name="fieldName">name of the field to be created</param>
        /// <param name="fieldValue">value of the field</param>
        /// <param name="binding">binding status of the field</param>
        public void CreateField(XString fieldName, long fieldValue, bool binding)
        {
            int retval = Imports.XAM_CreateInt(xamHandle, fieldName, binding, fieldValue);
            Helpers.CheckAndThrow(retval, "XAM_CreateInt");
        }

        /// <summary>
        /// Convenience method which creates an Int Property field on a XAMObject with a default status of binding 
        /// </summary>
        /// <param name="fieldName">name of the field to be created</param>
        /// <param name="fieldValue">value of the field</param>
        public void CreateField(XString fieldName, long fieldValue) { this.CreateField(fieldName, fieldValue, true); }

        /// <summary>
        /// Sets the value of an Int Property field
        /// </summary>
        /// <param name="fieldName">name of the field to be set</param>
        /// <param name="fieldValue">value the field is to be set to</param>
        public void SetField(XString fieldName, long fieldValue)
        {
            int retVal = Imports.XAM_SetInt(xamHandle, fieldName, fieldValue);
            Helpers.CheckAndThrow(retVal, "XAM_SetInt");
        }

        /// <summary>
        /// Gets the value of an Int Property field
        /// </summary>
        /// <param name="fieldName">name of the field to get</param>
        /// <param name="fieldValue">current value of the field</param>
        public void GetField(XString fieldName, out long fieldValue)
        {
            int retval = Imports.XAM_GetInt(xamHandle, fieldName, out fieldValue);
            Helpers.CheckAndThrow(retval, "XAM_GetInt");
        }
        #endregion

        #region Factory Methods
        /// <summary>
        /// Creates an XStream field on a XAMObject
        /// </summary>
        /// <param name="fieldName">name of the field to be created</param>
        /// <param name="mimeType">MIME type of the XStream</param>
        /// <param name="binding">binding status of the field</param>
        /// <returns>An XStream instance</returns>
        public XStream CreateXStream(XString fieldName, XString mimeType, bool binding)
        {
            XStreamHandle streamh;

            int retval = Imports.XAM_CreateXStream(xamHandle, fieldName, binding, mimeType, out streamh);
            Helpers.CheckAndThrow(retval, "XAM_CreateStream");

            return new XStream(xamHandle, fieldName, streamh, Constants.INPUT_STREAM);
        }

        /// <summary>
        /// Convenience method which creates an XStream field on a XAMObject with a default status of binding 
        /// </summary>
        /// <param name="fieldName">name of the field to be created</param>
        /// <param name="mimeType">MIME type of the XStream</param>
        /// <returns>An XStream instance</returns>
        public XStream CreateXStream(XString fieldName, XString mimeType) { return CreateXStream(fieldName, mimeType, true); }

        /// <summary>
        /// Creates a MultipartXStream object on a XAMObject
        /// </summary>
        /// <param name="fieldName">name of the MultipartXStream to be created</param>
        /// <param name="mimeType">MIME type of the MultipartXStream</param>
        /// <param name="parts">number of parts to split the Stream into</param>
        /// <param name="size">the final size of the MultipartXStream i.e. the amount of data that will be written to it</param>
        /// <param name="binding">binding status of the MultipartXStream</param>
        /// <returns>A MultipartXStream instance.</returns>
        public Helpers.MultipartXStream CreateMultipartXStream(XString fieldName, XString mimeType, int parts, long size, bool binding)
        {
            int i = 0, retval = 0;
            XStreamHandle xsh = 0;
            XStream[] streams = new XStream[parts];

            for (i = 0; i < parts; i++)
            {
                retval = Imports.XAM_CreateXStream(xamHandle, fieldName + "." + i, binding, mimeType, out xsh);
                Helpers.CheckAndThrow(retval, "XAM_CreateStream");

                streams[i] = new XStream(xamHandle, fieldName + "." + i, xsh, Constants.INPUT_STREAM);
            }

            return new Helpers.MultipartInputXStream(streams, size);
        }

        /// <summary>
        /// Convenience method which creates an MultipartXStream object on a XAMObject with a default status of binding 
        /// </summary>
        /// <param name="fieldName">name of the MultipartXStream to be created</param>
        /// <param name="mimeType">MIME type of the MultipartXStream</param>
        /// <param name="parts">number of parts to split the Stream into</param>
        /// <param name="size">the final size of the MultipartXStream i.e. the amount of data that will be written to it</param>
        /// <returns>A MultipartXStream instance.</returns>
        public Helpers.MultipartXStream CreateMultipartXStream(XString fieldName, XString mimeType, int parts, long size) { return CreateMultipartXStream(fieldName, mimeType, parts, size, true); }

        /// <summary>
        /// Opens an existing MultipartXStream field of a XAMObject in ReadOnly mode
        /// </summary>
        /// <param name="fieldName">name of the MultipartXStream field to open</param>
        /// <returns>A MultipartXStream instance</returns>
        public Helpers.MultipartXStream OpenMultipartXStream(XString fieldName)
        {
            XStream[] streams;

            using (XIterator xit = this.CreateXIterator(fieldName))
            {
                int i = 0, parts = 0;
                while (xit.MoveNext())
                {
                    parts++;
                }

                streams = new XStream[parts];
                xit.Reset();

                for (i = 0; i < parts; i++)
                {
                    xit.MoveNext();
                    streams[i] = XStream.Open(xamHandle, new XString((XString)xit.Current), Constants.XSTREAM_MODE_READ_ONLY);
                }
            }

            return new Helpers.MultipartOutputXStream(streams);
        }

        /// <summary>
        /// Opens an XStream field of an existing XAMObject
        /// </summary>
        /// <param name="fieldName">name of the XStream field to be opened</param>
        /// <param name="mode">mode to open the XStream in</param>
        /// <returns>An open XStream instance</returns>
        public XStream OpenXStream(XString fieldName, XString mode)
        {
            return XStream.Open(xamHandle, fieldName, mode);
        }

        /// <summary>
        /// Convenience method which opens an XStream field of an existing XAMObject in ReadOnly mode
        /// </summary>
        /// <param name="fieldName">name of the XStream field to be opened</param>
        /// <returns>An open XStream instance</returns>
        public XStream OpenXStream(XString fieldName)
        {
            return XStream.Open(xamHandle, fieldName, Constants.XSTREAM_MODE_READ_ONLY);
        }

        /// <summary>
        /// Asynchronously opens an XStream field of an existing XAMObject
        /// </summary>
        /// <param name="fieldName">name of the XStream field to be opened</param>
        /// <param name="mode">mode to open the XStream in</param>
        /// <param name="opID">a user supplied id / handle for this object</param>
        /// <param name="callback">name of the XAsyncCallback method to call when the operation completes</param>
        /// <returns>An XAsyncObject instance</returns>
        public XAsyncObject OpenXStream(XString fieldName, XString mode, long opID, XAsyncCallback callback)
        {
            XAsyncHandle xah;

            int retval = Imports.XAM_AsyncOpenXStream(xamHandle, fieldName, mode, opID, callback, out xah);
            Helpers.CheckAndThrow(retval, "XSystem_AsyncOpenXStream");

            if (mode == Constants.XSTREAM_MODE_READ_ONLY)
                return new XAsyncObject(xah, XAsyncType.XStreamOpenOutput, xamHandle, fieldName);
            else
                return new XAsyncObject(xah, XAsyncType.XStreamOpenInput, xamHandle, fieldName);
        }

        /// <summary>
        /// Creates an iterator on the XAMObject using the supplied filter
        /// </summary>
        /// <param name="pattern">the prefix filter / pattern for the field names the iterator will match </param>
        /// <returns>An XIterator instance</returns>
        public XIterator CreateXIterator(string pattern)
        {
            XIterator xit = new XIterator(xamHandle, pattern);

            return xit;
        }

        /// <summary>
        /// Creates a field info object that contains all the attributes of the existing field
        /// </summary>
        /// <param name="fieldName">name of the field to get the attributes for</param>
        /// <returns>An XFieldInfo intance</returns>
        public XFieldInfo GetFieldInfo(XString fieldName)
        {
            return new XFieldInfo(xamHandle, fieldName);
        }

        #endregion
    }
}

